package se.webbzon.oschi01.tools.mapper;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.util.Map.Entry;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import se.webbzon.oschi01.gui.ModelUtils;
import se.webbzon.oschi01.gui.SortedListModel;
import se.webbzon.oschi01.item.ItemEntry;
import se.webbzon.oschi01.item.ItemEntryComparator;
import se.webbzon.oschi01.item.ItemEntryMetric;
import se.webbzon.oschi01.metric.JaroWinklerMetric;
import se.webbzon.oschi01.metric.XWangJaroWinklerMetric;
import se.webbzon.oschi01.metric.XWangMetric;
import se.webbzon.oschi01.utils.Pair;

public class MapperPanel extends JPanel {
	
	private enum Algorithm {X_WANG, JARO_WINKLER, MIXED}; 
	
	private final MapperModel model;
	
	private final MapperSearcher searcher;
	
	private final JTextField searchField, toleranceField;
	
	private final JList<ItemEntry> fromList, toList;
		
	public MapperPanel(MapperModel model, MapperSearcher searcher) {
		this.model = model;
		this.searcher = searcher;
		
		setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
		Listener listener = new Listener();
		
		JPanel searchPanel = new JPanel();
		searchPanel.setLayout(new GridLayout(3,2));
		searchPanel.setMinimumSize(new Dimension(0,32));
		searchPanel.setMaximumSize(new Dimension(Integer.MAX_VALUE,32));
		searchPanel.add(new JLabel("Old item entries"));
		searchPanel.add(new JLabel("New item entries"));
		searchPanel.add(Box.createGlue());
		searchField = new JTextField();
		searchField.getDocument().addDocumentListener(listener);
		searchPanel.add(searchField);
		searchPanel.add(Box.createGlue());
		// Create radio panel
		JPanel radioPanel = new JPanel();
		radioPanel.add(new JLabel("Tolerance: "));
		toleranceField = new JTextField("" + searcher.getTolerance());
		toleranceField.getDocument().addDocumentListener(listener);
		radioPanel.add(toleranceField);
		radioPanel.setLayout(new BoxLayout(radioPanel,BoxLayout.X_AXIS));
		ButtonGroup group = new ButtonGroup();
		boolean select = true;
		for (Algorithm alg : Algorithm.values()) {
			JRadioButton radio = new JRadioButton(new MetricAction(alg));
			if (select) {
				radio.setSelected(select);
				select = false;
			}
			group.add(radio);
			radioPanel.add(radio);
		}
		searchPanel.add(radioPanel);
		
		add(searchPanel);
		
		JPanel listPanel = new JPanel();
		listPanel.setLayout(new GridLayout(1,2));
		fromList = new JList<ItemEntry>(ModelUtils.toSortedListModel(model.getUnmappedItems(), new ItemEntryComparator()));
		fromList.getSelectionModel().addListSelectionListener(listener);
		listPanel.add(new JScrollPane(fromList,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
		toList = new JList<ItemEntry>(new DefaultListModel<ItemEntry>());
		listPanel.add(new JScrollPane(toList,ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED));
		add(listPanel);
	}
	
	public Entry<ItemEntry,ItemEntry> getMapping() {
		ItemEntry oldEntry = fromList.getSelectedValue();
		ItemEntry newEntry = toList.getSelectedValue();
		if (oldEntry == null || newEntry == null)
			return null;
		else
			return new Pair<ItemEntry,ItemEntry>(oldEntry,newEntry);
	}
	
	public final void updateToList() {
		try {
			double t = Double.parseDouble(toleranceField.getText());
			if (searcher.setTolerance(t))
				toleranceField.setBackground(Color.white);
			else
				toleranceField.setBackground(Color.red);
		} catch (NumberFormatException e) {
			toleranceField.setBackground(Color.red);
		}
		
		String str = searchField.getText();
		ItemEntry entry = fromList.getSelectedValue();
		DefaultListModel<ItemEntry> lm = (DefaultListModel<ItemEntry>) toList.getModel();
		lm.clear();
		for (ItemEntry e : searcher.search(model, entry, str))
			lm.addElement(e);
	}
	
	public final void updateFromList(ItemEntry entry) {
		SortedListModel<ItemEntry> lm = (SortedListModel<ItemEntry>) fromList.getModel();
		boolean inModel = model.isMapped(entry);
		boolean inList = lm.contains(entry);
		if (inModel && inList)
			lm.remove(entry);
		else if (!inModel && !inList)
			lm.add(entry);
	}
	
	public final void updateFromList() {
		fromList.setModel(ModelUtils.toSortedListModel(model.getUnmappedItems(), new ItemEntryComparator()));
	}
	
	private final class MetricAction extends AbstractAction {
		
		/**
		 * 
		 */
		private static final long serialVersionUID = 3986791685615914542L;
		
		private final Algorithm alg;
		
		public MetricAction(Algorithm alg) {
			switch (alg) {
				case X_WANG: putValue(Action.NAME,"X Wang"); break;
				case JARO_WINKLER: putValue(Action.NAME,"Jaro-Winkler"); break;
				default: putValue(Action.NAME,"Mixed"); break;
			}
			this.alg = alg;
		}

		@Override public void actionPerformed(ActionEvent arg0) {
			ItemEntryMetric metric;
			switch (alg) {
				case X_WANG: metric = new ItemEntryMetric(new XWangMetric()); break;
				case JARO_WINKLER: metric = new ItemEntryMetric(new JaroWinklerMetric()); break;
				default: metric = new ItemEntryMetric(new XWangJaroWinklerMetric()); break;
			}
			searcher.setMetric(metric);
			updateToList();
		}
		
	}
	
	private final class Listener implements DocumentListener, ListSelectionListener {

		@Override public void changedUpdate(DocumentEvent arg0) {
			updateToList();
		}

		@Override public void insertUpdate(DocumentEvent arg0) {
			updateToList();
		}

		@Override public void removeUpdate(DocumentEvent arg0) {
			updateToList();
		}

		@Override public void valueChanged(ListSelectionEvent arg0) {
			updateToList();
		}
		
	}
	
}
